﻿using System.Collections.Generic;

namespace Spiral
{
    /// <summary>
    /// The class has the ability to determine the next step in a square matrix following the clockwise spiral pattern.
    /// </summary>
    public class MatrixNavigator
    {
        /// <summary>
        /// The possible movement directions on the spiral.
        /// </summary>
        private enum Direction
        {
            Right,
            Down,
            Left,
            Top
        }

        /// <summary>
        /// The current step.
        /// </summary>
        private Step currentStep;

        /// <summary>
        /// The matrix dimention.
        /// </summary>
        private int dimention;

        /// <summary>
        /// The current direction.
        /// </summary>
        private Direction direction;

        /// <summary>
        /// Keeps track of the number of turns made to the different directions
        /// </summary>
        private Dictionary<Direction, int> turns;

        /// <summary>
        /// The navigator constructor
        /// </summary>
        /// <param name="dimention">The dimention of the matrix.</param>
        public MatrixNavigator(int dimention)
        {
            this.currentStep = new Step { X = 1, Y = 1 };
            this.dimention = dimention;
            this.direction = Direction.Right;
            this.turns = new Dictionary<Direction, int>
            {
                {Direction.Right, 1},
                {Direction.Down, 0},
                {Direction.Left, 0},
                {Direction.Top, 0},
            };
        }

        public Step GetStep()
        {
            //Backup the current step coordinates
            var x = currentStep.X;
            var y = currentStep.Y;

            //Calculate the new current step
            CalculateNextStep();

            //Return the backed up current step
            return new Step() { X = x, Y = y };
        }

        private void CalculateNextStep()
        {
            //Is the current step is a corner, change direction according to the current direction.
            if (StepIsCorner())
            {
                switch (direction)
                {
                    //After mofing right, we must turn down.
                    case Direction.Right:
                        direction = Direction.Down;
                        turns[Direction.Down]++;
                        currentStep.Y++;
                        break;
                    //After mofing down, we must turn left.
                    case Direction.Down:
                        direction = Direction.Left;
                        turns[Direction.Left]++;
                        currentStep.X--;
                        break;
                    //After mofing left, we must turn top.
                    case Direction.Left:
                        direction = Direction.Top;
                        turns[Direction.Top]++;
                        currentStep.Y--;
                        break;
                    //After mofing top, we must turn right.
                    case Direction.Top:
                        direction = Direction.Right;
                        turns[Direction.Right]++;
                        currentStep.X++;
                        break;
                }
            }
            //The current step is not a corner, just increment step according to current direction
            else
            {
                switch (direction)
                {
                    //Move right
                    case Direction.Right:
                        currentStep.X++;
                        break;
                    //Move down
                    case Direction.Down:
                        currentStep.Y++;
                        break;
                    //Move left
                    case Direction.Left:
                        currentStep.X--;
                        break;
                    //Move top
                    case Direction.Top:
                        currentStep.Y--;
                        break;
                }
            }
        }

        /// <summary>
        /// Checks if the current step is a corner. A corner is determined also by the number of turns to the different directions.
        /// </summary>
        /// <returns></returns>
        private bool StepIsCorner()
        {
            return (direction == Direction.Right && currentStep.X == dimention - turns[Direction.Down])
                || (direction == Direction.Down && currentStep.Y == dimention - turns[Direction.Left])
                || (direction == Direction.Left && currentStep.X == 1 + turns[Direction.Top])
                || (direction == Direction.Top && currentStep.Y == 1 + turns[Direction.Right]);
        }
    }
}